# Profiler
# считает количество вызовов методов и время, которое ушло на их работу
#
# вызов: ^Profiler:do[имя_класса;еще_одно_имя_класса]
# исследуемый класс должен быть подключен в момент вызова профайлера

@CLASS
Profiler

@OPTIONS
locals


@auto[]
	$self.hProfile[^hash::create[]]
	$self.hClasses[^hash::create[]]
	$self.hCounterRel[^hash::create[]]
	$self.hCounterAbs[^hash::create[]]


@do[p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12;p13;p14;p15;p16;p17;p18;p19;p20]
	^for[i](1;20){
		^if(def ${p$i}){
			$cContext[^self.getContext[${p$i}]]
			^if($cContext is ${p$i}){
				^self.profiler[$cContext]
			}
		}
	}


@getContext[sClassName]
	^if(!def $self.hClasses.$sClassName){
		$MAIN:name[$sClassName]
		$self.hClasses.$sClassName[^process{^$${MAIN:name}:CLASS}]
	}
	$result[$self.hClasses.$sClassName]


@GET_table[]
	$Profiler:hTables[^hash::create[]]
	^Profiler:hProfile.foreach[sClassName;hClassProfile]{
		$fTimeAll(0)
		$Profiler:hTables.$sClassName[^table::create{method	time	all_time	count}]
		^hClassProfile.foreach[sMethodName;hData]{
			$fV($hData.fTime/1000)
			$fVSum($hData.fTimeSum/1000)
			^Profiler:hTables.$sClassName.append{^sMethodName.trim[start;@]	^fV.format[%.3f]	^fVSum.format[%.3f]	$hData.iCount}
			^fTimeAll.inc($fV)
		}
		^Profiler:hTables.$sClassName.sort{^Profiler:hTables.$sClassName.time.double(0)}[desc]
	}
	$result[$Profiler:hTables]


# принимает полный путь к файлу класса или путь относительно CLASS_PATH
@profiler[cContext]
	$start($status:rusage.tv_sec+$status:rusage.tv_usec/1000000)
	$sFilename[$cContext.CLASS_NAME]
	^MAIN:CLASS_PATH.menu{
		$sPath[$MAIN:CLASS_PATH.path/${sFilename}.p]
		^if(-f $sPath){
			$fClass[^file::load[binary;$sPath]]
			^markup[$cContext;$fClass.text]
		}
	}
	$end($status:rusage.tv_sec+$status:rusage.tv_usec/1000000)
	^rem{ $Profiler:hProfile._[
		$.fTime($end-$start)
	] }
	$result[]


# разметка (пока только для одного класса на файл)
@markup[cContext;sText]
	$mtc[^sText.match[^^@CLASS\n(\w+)^$][gm]]
	$sClassName[$mtc.1]
	
	^if($cContext.CLASS_NAME ne $sClassName){
		^throw[bad_context;;$cContext.CLASS_NAME ne $sClassName]
	}
	
	$Profiler:hProfile.$sClassName[^hash::create[]]
	
	$mtc[^sText.match[(?:^^|\n)@([\w\-]+)\[([\w^;\-]*)\](\[[\w^;\-]*\])?\n(.+?)(?=\n@|^$)][g'x]]

	^rem{ перебор методов }
	^mtc.menu{
		$sMethodName[$mtc.1]
		$sParams[$mtc.2]
		$sLocals[$mtc.3]
		$sCode[$mtc.4]
		
		^rem{ для auto профайлер не имеет смысла, т.к. для получения контекста класс нужно создать }
		^if($sMethodName eq auto){
			^continue[]
		}
		
		$Profiler:hProfile.$sClassName.@$sMethodName[
			$.iCount(0)
			$.iExceptionCount(0)
			$.fTime(0)
			$.fTimeSum(0)
		]

^sCode.save[/123.txt]
		
		^process[$cContext.CLASS]{@main[$sParams]$sLocals
^^Profiler:countIn[$sClassName^;@$sMethodName](^^status:rusage.tv_sec.double[]*1000+^^status:rusage.tv_usec.double[]/1000)
$sCode
^^Profiler:countOut[$sClassName^;@$sMethodName](^^status:rusage.tv_sec.double[]*1000+^^status:rusage.tv_usec.double[]/1000)
		}[
			$.main[$sMethodName]
		]
		
	}
	$result[]


@countIn[sClassName;sMethodName;fStartTime][result]
	^Profiler:hProfile.$sClassName.$sMethodName.iCount.inc[]
	$Profiler:hCounterRel.[^Profiler:hCounterRel._count[]]($fStartTime)
	$Profiler:hCounterAbs.[^Profiler:hCounterAbs._count[]]($fStartTime)
	$result[]


@countOut[sClassName;sMethodName;fEndTime][result]
	$iCurI(^Profiler:hCounterRel._count[]-1)
	$fStartTime($Profiler:hCounterRel.$iCurI)
	$fStartTimeSum($Profiler:hCounterAbs.$iCurI)
	$fTime($fEndTime - $fStartTime)
	$fTimeSum($fEndTime - $fStartTimeSum)
	^Profiler:hCounterRel.delete[$iCurI]
	^Profiler:hCounterAbs.delete[$iCurI]
	^Profiler:hCounterRel.foreach[i;fT]{
		$Profiler:hCounterRel.$i($fT+$fTime)
	}
	^Profiler:hProfile.$sClassName.$sMethodName.fTime.inc($fTime)
	^Profiler:hProfile.$sClassName.$sMethodName.fTimeSum.inc($fTimeSum)
	$result[]
	
